package worker

import (
	"fmt"
	"go-crontab/common"
	"time"
)

type Scheduler struct {
	JobEventChan     chan *common.JobEvent               // etcd任务事件队列
	JobPlanTable     map[string]*common.JobSchedulerPlan // 任务调度计划表
	JobExcutingTable map[string]*common.JobExcuteInfo    // 任务执行表
	JobResultChan    chan *common.JobExcuteResult
}

var (
	G_scheduler *Scheduler
)

// 处理内存中任务事件表
func (scheduler *Scheduler) handlerJobEvent(jobEvent *common.JobEvent) {
	var (
		err              error
		jobExcuteInfo    *common.JobExcuteInfo
		jobExcuting      bool
		jobSchedulerPlan *common.JobSchedulerPlan
		jobExist         bool
	)

	switch jobEvent.EventType {
	case common.JOB_EVENT_SAVE:
		if jobSchedulerPlan, err = common.BuildJobSchedulePlan(jobEvent.Job); err != nil {
			// TODO:记录错误信息
			return
		}
		scheduler.JobPlanTable[jobEvent.Job.Name] = jobSchedulerPlan
	case common.JOB_EVENT_DELETE:
		if jobSchedulerPlan, jobExist = scheduler.JobPlanTable[jobEvent.Job.Name]; jobExist {
			delete(scheduler.JobPlanTable, jobEvent.Job.Name)
		}
	case common.JOB_EVENT_KILL:
		fmt.Println("000 ", jobEvent.Job.Name)
		if jobExcuteInfo, jobExcuting = scheduler.JobExcutingTable[jobEvent.Job.Name]; jobExcuting {
			fmt.Println("111 ", jobEvent.Job.Name)
			jobExcuteInfo.CancelFunc() // 触发command杀死shell子进程，任务退出
		}
		fmt.Println(jobExcuteInfo, jobExcuting)
	}
}

// 尝试执行任务
func (scheduler Scheduler) TryStartJob(jobSchedulerPlan *common.JobSchedulerPlan) {
	var (
		jobExcuteInfo *common.JobExcuteInfo
		jobExcuting   bool
	)

	// 执行的任务可能运行很久，1分钟会调度60次，但是只能执行1次，防止并发！
	// 如果任务正在执行，跳过调度
	if jobExcuteInfo, jobExcuting = scheduler.JobExcutingTable[jobSchedulerPlan.Job.Name]; jobExcuting {
		//fmt.Println("尚未退出，跳过执行: ", jobExcuteInfo.Job.Name)
		return
	}

	jobExcuteInfo = common.BuildJobExcuteInfo(jobSchedulerPlan)
	scheduler.JobExcutingTable[jobSchedulerPlan.Job.Name] = jobExcuteInfo

	// 执行任务
	//fmt.Println("尝试执行任务: ", jobExcuteInfo.Job.Name, time.Now().Unix())
	G_excutor.ExcutorJob(jobExcuteInfo)
}

// 重新计算任务调度状态
func (scheduler *Scheduler) TryScheduler() (scheduleAfter time.Duration) {
	var (
		jobSchedulerPlan *common.JobSchedulerPlan
		now              = time.Now()
		nearTime         *time.Time
	)

	// 如果任务调度计划表为空，随便睡眠多久
	if len(scheduler.JobPlanTable) == 0 {
		scheduleAfter = 1 * time.Second
		return
	}

	// TODO: 隐藏BUG。如果map遍历时间大于周期执行的某类任务，则某类任务都不会被准时执行!! 不触发的条件：1)建议单机任务数量不超过1000w 2)优化数据结构:最小堆+hash,并支持按key查找及按score有序
	for _, jobSchedulerPlan = range scheduler.JobPlanTable {
		if jobSchedulerPlan.NextTime.Before(now) || jobSchedulerPlan.NextTime.Equal(now) {
			scheduler.TryStartJob(jobSchedulerPlan)
			jobSchedulerPlan.NextTime = jobSchedulerPlan.Expr.Next(now) // 更新下次调度时间
		}
		// 统计最近一个要过期的任务的时间
		if nearTime == nil || jobSchedulerPlan.NextTime.Before(*nearTime) {
			nearTime = &jobSchedulerPlan.NextTime
		}
	}

	// 距离最近任务执行时的时间差
	scheduleAfter = (*nearTime).Sub(now)
	return
}

func (scheduler *Scheduler) handlerJobResult(result *common.JobExcuteResult) {
	var (
		jobLog *common.JobLog
	)
	delete(scheduler.JobExcutingTable, result.ExcuteInfo.Job.Name)
	if result.Err != common.ERR_LOCK_ALREADY_REQUIRED {
		jobLog = &common.JobLog{
			JobName:      result.ExcuteInfo.Job.Name,
			Command:      result.ExcuteInfo.Job.Command,
			Output:       string(result.Output),
			PlanTime:     result.ExcuteInfo.PlanTime.UnixNano() / 1000 / 1000, // ms
			ScheduleTime: result.ExcuteInfo.RealTime.UnixNano() / 1000 / 1000, // ms
			StartTime:    result.StartTime.UnixNano() / 1000 / 1000,
			EndTime:      result.EndTime.UnixNano() / 1000 / 1000,
		}
		if result.Err != nil {
			jobLog.Err = result.Err.Error()
		} else {
			jobLog.Err = ""
		}
	}
	G_logSink.Append(jobLog)
	//fmt.Println("任务执行完成：", result.ExcuteInfo.Job.Name, string(result.Output), result.Err)
}

// 调度
func (scheduler *Scheduler) schedulerLoop() {
	var (
		jobEvent       *common.JobEvent
		scheduleAfter  time.Duration
		schedulerTimer *time.Timer
		jobResult      *common.JobExcuteResult
	)

	// ***扫描一次调度表
	scheduleAfter = scheduler.TryScheduler()
	// 调度延时的定时器
	schedulerTimer = time.NewTimer(scheduleAfter)

	for {
		select {
		case jobEvent = <-scheduler.JobEventChan:
			// 对内存中维护的任务列表做curd
			scheduler.handlerJobEvent(jobEvent)
		case <-schedulerTimer.C: // 最近的一个任务到期了
		case jobResult = <-scheduler.JobResultChan:
			scheduler.handlerJobResult(jobResult)
		}
		// ***再扫描一次调度表
		scheduleAfter = scheduler.TryScheduler()
		schedulerTimer.Reset(scheduleAfter) // 重置延时定时器
	}
}

// 推送任务变化事件
func (scheduler *Scheduler) PushJobEvent(jobEvent *common.JobEvent) {
	scheduler.JobEventChan <- jobEvent
}

func InitScheduler() (err error) {
	G_scheduler = &Scheduler{
		JobEventChan:     make(chan *common.JobEvent, 1000),
		JobPlanTable:     make(map[string]*common.JobSchedulerPlan),
		JobExcutingTable: make(map[string]*common.JobExcuteInfo),
		JobResultChan:    make(chan *common.JobExcuteResult, 1000),
	}

	go G_scheduler.schedulerLoop()
	return
}

// 回传任务执行结果
func (scheduler *Scheduler) PushJobResult(jobResult *common.JobExcuteResult) {
	scheduler.JobResultChan <- jobResult
}
